package com.account.book.service.impl;

import cn.hutool.core.bean.BeanUtil;
import com.account.book.common.exception.BasicInfoException;
import com.account.book.common.exception.BasicInfoStatusEnum;
import com.account.book.common.response.BaseResult;
import com.account.book.entity.Book;
import com.account.book.entity.BookMoney;
import com.account.book.entity.BookUser;
import com.account.book.entity.Category;
import com.account.book.enums.*;
import com.account.book.mapper.BookMoneyMapper;
import com.account.book.request.BookMoneyQueryReqVo;
import com.account.book.response.BookMoneyCountRes;
import com.account.book.response.BookMoneyResSubVo;
import com.account.book.response.BookMoneyResVo;
import com.account.book.response.BookUserQueryVo;
import com.account.book.service.BookMoneyService;
import com.account.book.service.BookService;
import com.account.book.service.BookUserService;
import com.account.book.service.CategoryService;
import com.account.book.vo.ResultPage;
import com.baomidou.mybatisplus.core.conditions.Wrapper;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.UpdateWrapper;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.github.pagehelper.PageHelper;
import com.github.pagehelper.PageInfo;
import ma.glasnost.orika.MapperFacade;
import org.apache.commons.lang3.ObjectUtils;
import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.math.BigDecimal;
import java.time.LocalDate;
import java.time.YearMonth;
import java.time.format.DateTimeFormatter;
import java.util.*;
import java.util.stream.Collectors;

/**
 * @author liang-hw
 * @className BookMoneyServiceImpl
 * @description 账本记账金额表 服务实现类
 * @date 2023/11/09 10:12:58
 */
@Service
public class BookMoneyServiceImpl extends ServiceImpl<BookMoneyMapper, BookMoney> implements BookMoneyService {
    private static final Logger logger = LoggerFactory.getLogger(BookMoneyServiceImpl.class);
    @Autowired
    private CategoryService categoryService;
    @Autowired
    private BookService bookService;
    @Autowired
    private BookMoneyMapper bookMoneyMapper;
    @Autowired
    private BookUserService bookUserService;
    @Autowired
    private MapperFacade mapperFacade;

    /**
     * @param @param bookMoneyQueryReqVo
     * @return @return {@link BookMoneyCountRes }
     * @author liang-hw
     * @description 查询账本对应数据
     * @date 2023/11/06 15:56:12
     */
    @Override
    public BookMoneyCountRes findList(BookMoneyQueryReqVo bookMoneyQueryReqVo) {
        if (bookMoneyQueryReqVo.getBookId() == null) {
            logger.error("查询失败，id参数未传递,bookId:", bookMoneyQueryReqVo.getBookId());
            throw new BasicInfoException(BasicInfoStatusEnum.SYSTEM_ERROR.getCode(),
                    "请选择账本进行查询");
        }
        BookMoneyCountRes bookMoneyCountRes = new BookMoneyCountRes();
        if (StringUtils.isNotEmpty(bookMoneyQueryReqVo.getBookTime())) {
            dealDate(bookMoneyQueryReqVo);
        }
        // 分页数据
        ResultPage<BookMoneyResVo> data = this.findPage(bookMoneyQueryReqVo);
        bookMoneyCountRes.setBookMoneyList(data);
        return bookMoneyCountRes;
    }

    /**
     * @param @param bookMoneyQueryReqVo
     * @return @return {@link BookMoneyCountRes }
     * @author liang-hw
     * @description 查询收支金额
     * @date 2023/11/06 17:29:27
     */
    @Override
    public BookMoneyCountRes total(BookMoneyQueryReqVo bookMoneyQueryReqVo, String userId) {
        if (bookMoneyQueryReqVo.getBookId() == null) {
            logger.error("查询失败，id参数未传递,bookId:", bookMoneyQueryReqVo.getBookId());
            throw new BasicInfoException(BasicInfoStatusEnum.SYSTEM_ERROR.getCode(),
                    "请选择账本进行查询");
        }
        BookMoneyCountRes bookMoneyCountRes = new BookMoneyCountRes();
        if (StringUtils.isNotEmpty(bookMoneyQueryReqVo.getBookTime())) {
            dealDate(bookMoneyQueryReqVo);
        }
        //查询收入
        BigDecimal selectIncome = baseMapper.getBookMoney(bookMoneyQueryReqVo.getBookId(), bookMoneyQueryReqVo.getStartQueryTime(), bookMoneyQueryReqVo.getEndQueryTime(), BookMoneyTypeEnum.INCOME.getValue());
        //查询支出
        BigDecimal selectExpenditure = baseMapper.getBookMoney(bookMoneyQueryReqVo.getBookId(), bookMoneyQueryReqVo.getStartQueryTime(), bookMoneyQueryReqVo.getEndQueryTime(), BookMoneyTypeEnum.DISBURSE.getValue());
        //查询账本额度
        BookUserQueryVo bookUserQueryVo = bookUserService.getBookOne(bookMoneyQueryReqVo.getBookId(), userId);
        if (ObjectUtils.isNotEmpty(bookUserQueryVo)) {
            bookMoneyCountRes.setBudget(bookUserQueryVo.getBudget());
            if (ObjectUtils.isNotEmpty(bookUserQueryVo.getBudget())) {
                BigDecimal surplusBudget = bookUserQueryVo.getBudget().subtract(selectExpenditure);
                if (surplusBudget.compareTo(BigDecimal.ZERO) < 0) {
                    bookMoneyCountRes.setIsBudget(true);
                }
                bookMoneyCountRes.setSurplusBudget(surplusBudget);
            }
        }
        bookMoneyCountRes.setSelectIncome(selectIncome);
        bookMoneyCountRes.setSelectExpenditure(selectExpenditure);
        return bookMoneyCountRes;
    }


    /**
     * @param @param bookMoneyQueryReqVo
     * @return
     * @author liang-hw
     * @description 记一笔
     * @date 2023/11/09 10:21:34
     */
    @Override
    public void addSave(BookMoneyQueryReqVo bookMoneyQueryReqVo, String userId) {
        BookMoney bookMoney = new BookMoney();
        BeanUtil.copyProperties(bookMoneyQueryReqVo, bookMoney);
        Category category = IFNull(bookMoney.getBookId(), bookMoney.getCategoryId());
        bookMoney.setCategoryName(category.getName());  // 分类名称
        bookMoney.setAvatarUrl(category.getIcon());// 分类图标
        bookMoney.setCreateTime(new Date());
        bookMoney.setUpdateTime(new Date());
        bookMoney.setStatus(DataStatusEnum.ENABLE.getValue());
        bookMoney.setCreateBy(userId);
        bookMoney.setUpdateBy(userId);
        IFType(bookMoney.getType());
        // 如果富文本值为空，设置0
        if (StringUtils.isEmpty(bookMoney.getDetailDesc())) {
            bookMoney.setDetailDescFlag("0");
        } else {
            bookMoney.setDetailDescFlag("1");
        }
        this.save(bookMoney);
    }


    /**
     * @param @param bookMoneyQueryReqVo
     * @param userId
     * @return
     * @author liang-hw
     * @description 修改账单
     * @date 2024/01/11 09:54:30
     */
    @Override
    @Transactional
    public void update(BookMoneyQueryReqVo bookMoneyQueryReqVo, String userId) {
        BookMoney bookMoney = this.getById(bookMoneyQueryReqVo.getId());
        // 判断是否是管理员才能修改，否则需要返回权限提示
        isBookAuthor(bookMoney, userId, "仅管理员修改");
        Category category = IFNull(bookMoneyQueryReqVo.getBookId(), bookMoneyQueryReqVo.getCategoryId());
        IFType(bookMoneyQueryReqVo.getType());
        BeanUtil.copyProperties(bookMoneyQueryReqVo, bookMoney, "id", "bookId", "status", "createBy", "createTime");
        bookMoney.setCategoryName(category.getName());  // 分类名称
        bookMoney.setAvatarUrl(category.getIcon());// 分类图标
        bookMoney.setUpdateBy(userId); // 更新人
        bookMoney.setUpdateTime(new Date());//更新时间
        // 如果富文本值为空，设置0，否则设置1
        if (StringUtils.isEmpty(bookMoney.getDetailDesc())) {
            bookMoney.setDetailDescFlag("0");
        } else {
            bookMoney.setDetailDescFlag("1");
        }
        UpdateWrapper<BookMoney> bookMoneyUpdateWrapper = new UpdateWrapper<>();
        bookMoneyUpdateWrapper.lambda()
                .eq(BookMoney::getId, bookMoneyQueryReqVo.getId());
        this.update(bookMoney, bookMoneyUpdateWrapper);
    }


    @Override
    public void delete(String moneyId, String userId) {
        BookMoney bookMoney = this.getById(moneyId);
        if (null == bookMoney) {
            logger.error("账本流水（账本金额）不存在，book_money的id：{}", moneyId);
            throw new BasicInfoException(BasicInfoStatusEnum.SYSTEM_ERROR.getCode(),
                    "流水不存在");
        }
        //校验是账单否为创建者或者管理员
        isBookAuthor(bookMoney, userId, "仅管理员删除");
        UpdateWrapper<BookMoney> bookMoneyUpdateWrapper = new UpdateWrapper<>();
        bookMoneyUpdateWrapper.lambda()
                .eq(BookMoney::getId, moneyId)
                .set(BookMoney::getStatus, DataStatusEnum.DELETE.getValue());
        this.update(null, bookMoneyUpdateWrapper);
    }

    /**
     * @param @param bookMoneyQueryReqVo
     * @return @return {@link BaseResult }<{@link ResultPage }<{@link BookMoneyResVo }>>
     * @author liang-hw
     * @description le:小于等于 <=
     * <p>
     * ge:大于等于 >=
     * <p>
     * String format = DateUtil.format(new Date(), "yyyy-MM-dd");
     * @date 2023/11/09 10:13:42
     */
    public ResultPage<BookMoneyResVo> findPage(BookMoneyQueryReqVo bookMoneyQueryReqVo) {
        List<BookMoneyResVo> bookMoneyResVoListOne = new ArrayList<>();
        PageHelper.startPage(bookMoneyQueryReqVo.getPageNumber(), bookMoneyQueryReqVo.getPageSize());
        List<BookMoney> bookMoneyList = this.list(queryWrapper(bookMoneyQueryReqVo));
        PageInfo pageInfo = new PageInfo(bookMoneyList);
        List<BookMoneyResSubVo> bookMoneyResSubVoList = mapperFacade.mapAsList(bookMoneyList, BookMoneyResSubVo.class);
        //按照时间进行分组
        Map<Date, List<BookMoneyResSubVo>> bookMoneyResSubVoListMap = bookMoneyResSubVoList.stream().collect(Collectors.groupingBy(BookMoneyResSubVo::getBookTime));
        bookMoneyResSubVoListMap.forEach((key, value) -> {
            Map<Integer, BigDecimal> bookMoneyResSubGroup = value.stream().collect(Collectors.groupingBy(BookMoneyResSubVo::getType
                    , Collectors.mapping(BookMoneyResSubVo::getMoney, Collectors.reducing(BigDecimal.ZERO, BigDecimal::add))));
            BookMoneyResVo bookMoneyResVo = new BookMoneyResVo();
            bookMoneyResVo.setBookTime(key);
            bookMoneyResVo.setDisburseMoney(bookMoneyResSubGroup.get(1));
            bookMoneyResVo.setIncomeMoney(bookMoneyResSubGroup.get(0));
            bookMoneyResVo.setBookMoneyResSubVoList(value);
            bookMoneyResVoListOne.add(bookMoneyResVo);
        });
        List<BookMoneyResVo> bookMoneyResVoList = bookMoneyResVoListOne.stream().sorted(Comparator.comparing(BookMoneyResVo::getBookTime).reversed()).collect(Collectors.toList());
        return new ResultPage<>(pageInfo.getTotal(), pageInfo.getPages(), pageInfo.getPageNum(), pageInfo.getPageSize(), bookMoneyResVoList);
    }

    private Wrapper<BookMoney> queryWrapper(BookMoneyQueryReqVo bookMoneyQueryReqVo) {
        return new QueryWrapper<BookMoney>().lambda()
                .eq(null != bookMoneyQueryReqVo.getBookId(), BookMoney::getBookId, bookMoneyQueryReqVo.getBookId())
                .in(null != bookMoneyQueryReqVo.getCategoryIds() && bookMoneyQueryReqVo.getCategoryIds().size() != 0, BookMoney::getCategoryId, bookMoneyQueryReqVo.getCategoryIds())
                .eq(BookMoney::getStatus, DataStatusEnum.ENABLE.getValue())
                .eq(null != bookMoneyQueryReqVo.getType() && bookMoneyQueryReqVo.getType() != -1, BookMoney::getType, bookMoneyQueryReqVo.getType())
                .le(null != bookMoneyQueryReqVo.getEndQueryTime(), BookMoney::getBookTime, bookMoneyQueryReqVo.getEndQueryTime())
                .ge(null != bookMoneyQueryReqVo.getStartQueryTime(), BookMoney::getBookTime, bookMoneyQueryReqVo.getStartQueryTime()
                ).orderByDesc(BookMoney::getBookTime);
    }

    /**
     * @param @param bookMoneyQueryReqVo
     * @return
     * @author liang-hw
     * @description 根据入参时间整理时间
     * @date 2023/11/07 08:52:20
     */
    private void dealDate(BookMoneyQueryReqVo bookMoneyQueryReqVo) {
        // 转换为YearMonth对象
        YearMonth yearMonth = YearMonth.parse(bookMoneyQueryReqVo.getBookTime(), DateTimeFormatter.ofPattern("yyyy-MM"));
        // 获取指定月份的第一天
        LocalDate firstDay = yearMonth.atDay(1);
        // 获取指定月份的最后一天
        LocalDate lastDay = yearMonth.atEndOfMonth();
        bookMoneyQueryReqVo.setStartQueryTime(firstDay.toString());
        bookMoneyQueryReqVo.setEndQueryTime(lastDay.toString());
    }

    /**
     * 检验账本，分类是否存在
     *
     * @param bookId
     * @param categoryId
     */
    private Category IFNull(String bookId, String categoryId) {
        if (StringUtils.isNotEmpty(bookId)) {
            Book book = bookService.getById(bookId);
            if (null == book) {
                logger.error("记账失败，账本不存在,账本id:{}", bookId);
                throw new BasicInfoException(BasicInfoStatusEnum.SYSTEM_ERROR.getCode(),
                        "记账失败，账本不存在");
            }
        }
        QueryWrapper<Category> queryWrapper = new QueryWrapper();
        queryWrapper.lambda().eq(Category::getCategoryId, categoryId)
                .eq(Category::getBookId, bookId)
                .eq(Category::getStatus, CategoryBaseEnum.ENABLE.getValue());
        Category category = categoryService.getOne(queryWrapper);
        if (null == category) {
            logger.error("记账失败，分类不存在，分类id:{}", categoryId);
            throw new BasicInfoException(BasicInfoStatusEnum.SYSTEM_ERROR.getCode(),
                    "记账失败，分类不存在");
        }
        return category;
    }

    /**
     * 校验收入类型是否合法
     *
     * @param type
     */
    private void IFType(Integer type) {
        if (!EnumTemplate.existsByValue(BookMoneyTypeEnum.values(), Integer.valueOf(type))) {
            logger.error("参数:type: {} 不合法,只能传递0,1", type);
            throw new BasicInfoException(BasicInfoStatusEnum.SYSTEM_ERROR.getCode(),
                    "参数:type:" + type + "不合法,只能传递0,1");
        }
    }

    /**
     * @param @param bookMoneys
     * @param userId
     * @param errMeg
     * @return
     * @author liang-hw
     * @description 校验账单是否为创建者或者管理员
     * @date 2024/01/10 18:01:30
     */
    private void isBookAuthor(BookMoney bookMoneys, String userId, String errMeg) {
        // 如果创建人是当前操作人，允许删除
        if (bookMoneys.getCreateBy().equals(userId)) {
            return;
        }
        // 获取到bookId
        String bookId = bookMoneys.getBookId();
        BookUser bookUser = bookUserService.getOne(new QueryWrapper<BookUser>().lambda()
                .ne(BookUser::getAuth, BookAuthEnum.USER.getValue())
                .eq(BookUser::getUserId, userId)
                .eq(BookUser::getBookId, bookId)
                .eq(BookUser::getStatus, DataStatusEnum.ENABLE.getValue())
        );
        // 是成员
        if (ObjectUtils.isEmpty(bookUser)) {
            logger.error("用户：{}，在账本：{}的权限为：{}，{}", userId, bookUser.getAuth(), BookAuthEnum.USER.getTitle());
            throw new BasicInfoException(BasicInfoStatusEnum.NO_AUTH.getCode(),
                    errMeg);
        }
    }
}
